回顧一下昨天,我根據不同的request類型而使用不同的cache strategies:
當然目前server是假的,之後我會再建一個server來存放PWA中的用戶貼文資訊。
接著我還想再針對App shell中的靜態資源使用「cache only strategies」,因為這些靜態資源其實都不太會更新變動,就算更新了由於我pre-caching是寫在service worker的install階段,所以每次更改完靜態資源的程式碼後,service worker都會在重新install and activate。這樣static cache的版本也會順帶更新了。
所以針對這些資源我想非常適合不須透過網路直接從cache取得的策略。我直接在之前if-else中再加入一層判斷是否request url是我預先設定好的pre-cache靜態資源(是的話我希望只要從cache中取得就好):
// Application shell
var STATIC_FILES = [
'/',
'/index.html',
'/offline.html',
'/src/js/app.js',
'/src/js/feed.js',
'/src/js/promise.js',
'/src/js/fetch.js',
'/src/js/material.min.js',
'/src/css/app.css',
'/src/css/feed.css',
'/src/images/main-image.jpg',
'https://fonts.googleapis.com/css?family=Noto+Sans+TC&display=swap',
'https://fonts.googleapis.com/icon?family=Material+Icons',
'https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css'
]
self.addEventListener('fetch', function(event) {
var url = 'https://httpbin.org/get';
if(event.request.url.indexOf(url) > -1) {
// Cache then Network
event.respondWith(
caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
return fetch(event.request).then(function(res) {
cache.put(event.request, res.clone());
return res;
});
})
);
} else if(isInArray(event.request.url, STATIC_FILES)) { // 新增的判斷式
// Cache Only
event.respondWith(
caches.match(event.request)
);
} else {
// Cache with Network Fallback
event.respondWith(
caches.match(event.request).then(function(response) {
if(response) {
return response;
} else {
return fetch(event.request).then(function(res) {
return caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
cache.put(event.request.url, res.clone());
return res;
})
}).catch(function(err) {
return caches.open(CACHE_STATIC_NAME).then(function(cache) {
if(event.request.headers.get('accept').includes('text/html')) {
return cache.match('/offline.html');
}
});
});
}
})
);
}
});
我這裡又另外寫了一個isInArray() function來判斷輸入的url是否存在於STATIC_FILES陣列中。
function isInArray(string, array) {
var cachePath;
if (string.indexOf(self.origin) === 0) { // request的domain是否與我們PWA的domain相同
console.log('matched ', string);
cachePath = string.substring(self.origin.length); // 將domain之後的url擷取出來,也就是localhost:8080之後的url
} else {
cachePath = string; // 儲存完整的request(也就是外部的CDNs)
}
return array.indexOf(cachePath) > -1;
}
另外我還做了一個小小的improvement,可以看到在前面我的strategy code中當fetch request發生網路錯誤時,會跳到catch exception裡。而裡面我們可以在去判斷如果今天的request希望回傳的response是一個html網頁的話,就回傳offline.html。
我們可以發現的在PWA中dymanic cache隨著網站功能越來越多,暫存的東西也會越來越多。因此為了避免塞爆瀏覽器的cache,我這裡又寫了一個function來「限制dymanic cache可容納的最大筆數」:
function trimCache(cacheName, maxItems) {
caches.open(cacheName).then(function(cache) {
return cache.keys().then(function(keys) {
if(keys.length > maxItems) {
cache.delete(keys[0]).then(trimCache(cacheName, maxItems));
}
});
})
}
接著在我前面寫的cache strategies中,每次要put新request到dynamic cache之前,先執行trimCache() function。
trimCache(CACHE_DYNAMIC_NAME, 20); // 以我的PWA project來說,較合理的筆數大約是20筆
cache.put(event.request, res.clone());
cache大致上就介紹到這邊,接下來是要來練習如何使用browser中的indexedDB來儲存我PWA project中的動態資料(Dynamic Data)。
Day13 結束!!